﻿/*
Copyright (c) 2010 Lance Ward
This software is licensed under the MIT license, except where otherwise noted.
For complete license details, refer to license.txt
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ADNLib;

namespace ADNTimeline
{
    public partial class Timeline : UserControl
    {
        private ItemList items = new ItemList();
        private VScrollBar vScroll = new VScrollBar();
        private HScrollBar hScroll = new HScrollBar();
        private ItemFormat format = new ItemFormat();
        private int selectedIndex = -1;
        private int yTotal = 0;
        private int yOffset = 0;

		public delegate void TimelineEventHandler(object sender, TimelineEventArgs e);
		//public event TimelineEventHandler FrameLoaded;			//frame index
		public event TimelineEventHandler SelectionChanged;		//handles frame and layer selection changes, since one can't occur without the other
		public event TimelineEventHandler FrameAdded;			//new frame index
		public event TimelineEventHandler FrameDuplicated;		//original frame index, new frame index
		public event TimelineEventHandler FrameRemoved;			//frame index
		public event TimelineEventHandler FrameMoved;			//frame index, start frame, span
		public event TimelineEventHandler FramesShifted;		//shift amount
		public event TimelineEventHandler LayerAdded;			//empty
		public event TimelineEventHandler LayerRemoved;			//layer index
		public event TimelineEventHandler LayerMoved;			//original layer index, new layer index
		public event TimelineEventHandler LayerVisibleChanged;	//layer index
		public event TimelineEventHandler LayerLockChanged;		//layer index
		public event TimelineEventHandler OnionSkinChanged;		//skin option
		public event TimelineEventHandler RulerSelectionChanged;	//selected frame
		

        public Timeline(ItemFormat format)
        {
            InitializeComponent();
            InitializeControl();

            if (format != null) { this.format = format; }
        }

        public Timeline()
        {
            InitializeComponent();
            InitializeControl();
        }

        public ItemList Items
        {
            get { return items; }
            set { items = value; }
        }

        public ItemFormat Format
        {
            get { return format; }
            set { format = value; }
        }

		public int OnionSkinFrameIndex
		{
			get { return this.timeRuler.SelectedFrame; }
		}

		public int LoadedFrameIndex
		{
			get { return items[selectedIndex].Frame.SelectedFrame; }
		}

		public int LoadedFrameLayer
		{
			get { return selectedIndex; }	//TODO: the loaded layer is not necessary the selected layer.
		}

        public int SelectedIndex
        {
            get { return selectedIndex; }
            set { selectedIndex = value; }
        }

		//private void OnFrameLoaded()
		//{
		//    TimelineEventArgs e = new TimelineEventArgs();
		//    e.LayerIndex = selectedIndex;
		//    e.FrameIndex = items[selectedIndex].Frame.SelectedFrame;
		//    if (FrameLoaded != null) { FrameLoaded(this, e); }
		//}

        private void OnSelectionChanged()
        {
            TimelineEventArgs e = new TimelineEventArgs();
            e.LayerIndex = selectedIndex;
            e.FrameIndex = items[selectedIndex].Frame.SelectedFrame;
            if (SelectionChanged != null) { SelectionChanged(this, e); }
        }

		private void OnFrameAdded()
		{
			TimelineEventArgs e = new TimelineEventArgs();
            e.LayerIndex = selectedIndex;
            e.FrameIndex = items[selectedIndex].Frame.SelectedFrame;
            e.FrameStart = items[selectedIndex].Frame.Frames[e.FrameIndex].StartCell;
            e.FrameSpan = items[selectedIndex].Frame.Frames[e.FrameIndex].Span;
			if (FrameAdded != null) { FrameAdded(this, e); }
		}

		private void OnFrameDuplicated()
		{
            TimelineEventArgs e = new TimelineEventArgs();
            e.LayerIndex = selectedIndex;
            e.FrameIndex = items[selectedIndex].Frame.SelectedFrame;
            e.FrameStart = items[selectedIndex].Frame.Frames[e.FrameIndex].StartCell;
            e.FrameSpan = items[selectedIndex].Frame.Frames[e.FrameIndex].Span;
			if (FrameDuplicated != null) { FrameDuplicated(this, e); }
		}

		private void OnFrameRemoved()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (FrameRemoved != null) { FrameRemoved(this, e); }
		}

		private void OnFrameMoved()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			e.LayerIndex = selectedIndex;
			e.FrameIndex = items[selectedIndex].Frame.SelectedFrame;
			e.FrameStart = items[selectedIndex].Frame.Frames[e.FrameIndex].StartCell;
			e.FrameSpan = items[selectedIndex].Frame.Frames[e.FrameIndex].Span;
			if (FrameMoved != null) { FrameMoved(this, e); }
		}

		private void OnFramesShifted()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (FramesShifted != null) { FramesShifted(this, e); }
		}

		private void OnLayerAdded()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (LayerAdded != null) { LayerAdded(this, e); }
		}

		private void OnLayerRemoved()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (LayerRemoved != null) { LayerRemoved(this, e); }
		}

		private void OnLayerMoved()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (LayerMoved != null) { LayerMoved(this, e); }
		}

		private void OnLayerVisibleChanged()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (LayerVisibleChanged != null) { LayerVisibleChanged(this, e); }
		}

		private void OnLayerLockChanged()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (LayerLockChanged != null) { LayerLockChanged(this, e); }
		}

		private void OnOnionSkinChanged()
		{
			TimelineEventArgs e = new TimelineEventArgs();
			if (OnionSkinChanged != null) { OnionSkinChanged(this, e); }
		}

		private void OnRulerSelectionChanged()
		{
			TimelineEventArgs e = new TimelineEventArgs();
            e.LayerIndex = selectedIndex;
            e.FrameIndex = items[selectedIndex].Frame.SelectedFrame;
            e.OnionSkinFrame = this.timeRuler.SelectedFrame;
			if (RulerSelectionChanged != null) { RulerSelectionChanged(this, e); }
		}

        private void InitializeControl()
        {
            //implemented this way to allow adding to the list from outside the control
            items.ItemAdded += new ItemList.ItemListEventHandler(items_ItemAdded);
            items.ItemMoved += new ItemList.ItemListEventHandler(items_ItemMoved);
            items.ItemRemoved += new ItemList.ItemListEventHandler(items_ItemRemoved);
            items.ListCleared += new EventHandler(items_ListCleared);

            this.SuspendLayout();
            
            vScroll.Dock = DockStyle.Right;
            vScroll.Scroll += new ScrollEventHandler(vScroll_Scroll);
            vScroll.MouseWheel += new MouseEventHandler(vScroll_MouseWheel);
            vScroll.Visible = false;
            vScroll.SmallChange = format.ItemHeight;
            vScroll.LargeChange = format.ItemHeight;
            layersPanel.Controls.Add(vScroll);

            
            hScroll.Dock = DockStyle.Bottom;
            hScroll.Scroll += new ScrollEventHandler(hScroll_Scroll);
            hScroll.Maximum = 1000;
            hScroll.Minimum = 0;
            hScroll.SmallChange = 5;    //1 frame advance
            hScroll.LargeChange = 50;   //25 frame advance
            framesPanel.Controls.Add(hScroll);

            this.ResumeLayout();
        }

        void hScroll_Scroll(object sender, ScrollEventArgs e)
        {
            timeRuler.StartNumber = e.NewValue;
            //TODO: change start frame of frameitems
			for (int i = 0; i < items.Count; i++)
			{
				items[i].Frame.FrameOffset = e.NewValue;
			}
        }

		void vScroll_MouseWheel(object sender, MouseEventArgs e)
        {
            
        }

        void vScroll_Scroll(object sender, ScrollEventArgs e)
        {
            yOffset = -e.NewValue;
            ResizeItems();
        }

        void items_ListCleared(object sender, EventArgs e)
        {
            SetSelected(-1);

            for(int i = 0; i < framesPanel.Controls.Count; i++)
            {
                if(framesPanel.Controls[i].GetType() == typeof(FrameItem))
                {
                    framesPanel.Controls.RemoveAt(i);
                }
            }

            for(int i = 0; i < layersPanel.Controls.Count; i++)
            {
                if (layersPanel.Controls[i].GetType() == typeof(LayerItem))
                {
                    layersPanel.Controls.RemoveAt(i);
                }
            }

            SetLayerScroll();
        }

        void items_ItemRemoved(object sender, ItemListEventArgs e)
        {
            framesPanel.Controls.Remove(e.Item.Frame);
            layersPanel.Controls.Remove(e.Item.Layer);
            SetLayerScroll();
            ResizeItems();
        }

        void items_ItemMoved(object sender, ItemListEventArgs e)
        {
            SetSelected(e.CurrentIndex);
            RepositionItems(e.CurrentIndex - 1);
        }

        void items_ItemAdded(object sender, ItemListEventArgs e)
        {
            e.Item.Frame.Location = new Point(0, GetNextY(e.CurrentIndex));   //test
            e.Item.Frame.Height = format.ItemHeight;
            e.Item.Frame.Width = framesPanel.ClientRectangle.Width;
            e.Item.Frame.ItemSelected += new EventHandler(Item_ItemSelected);
			e.Item.Frame.FrameAdded += new EventHandler(Frame_FrameAdded);
            e.Item.Frame.FrameMoved += new EventHandler(Frame_FrameMoved);
            SetSelected(e.CurrentIndex);
            framesPanel.Controls.Add(e.Item.Frame);

            e.Item.Layer.Text = "Layer" + items.Count.ToString();
            e.Item.Layer.Location = new Point(0, GetNextY(e.CurrentIndex));   //test
            e.Item.Layer.Height = format.ItemHeight;
            e.Item.Layer.Width = layersPanel.ClientRectangle.Width;
            e.Item.Layer.ItemSelected +=new EventHandler(Item_ItemSelected);
            SetSelected(e.CurrentIndex);
            yTotal += e.Item.Layer.Height;
            SetLayerScroll();
            ResizeItems();
            layersPanel.Controls.Add(e.Item.Layer);
			
        }

        void Frame_FrameMoved(object sender, EventArgs e)
        {
			OnFrameMoved();
        }

		void Frame_FrameAdded(object sender, EventArgs e)
		{
			OnFrameAdded();
		}

        void  Item_ItemSelected(object sender, EventArgs e)
        {
            int index = GetItemIndex(sender);
            SetSelected(index);
            OnSelectionChanged();
        }

        private int GetItemIndex(object item)
        {
            for (int i = 0; i < items.Count; i++)
            {
                if (items[i].Frame == item || items[i].Layer == item)
                {
                    return i;
                }
            }

            return -1;
        }

        public void SetSelected(int index)
        {
            TimelineItem prev = null;
            TimelineItem selected = null;

            if (items.Count > index && index >= 0)
            {
                if (selectedIndex >= 0 && selectedIndex < items.Count)
                {
                    prev = items[selectedIndex];
                    prev.Frame.Selected = false;
                    prev.Layer.BackColor = format.ItemBackColor;
                    prev.Layer.Deselect();
                }

                selected = items[index];
                selected.Frame.Selected = true;
                selected.Frame.Focus();
                selected.Layer.BackColor = format.ItemSelectedBackColor;
                selected.Layer.Focus();
                selectedIndex = index;
            }
        }

        private void SetLayerScroll()
        {
            if ((items.Count * format.ItemHeight) > layersPanel.Bounds.Height)
            {
                vScroll.Visible = true;
            }
            else
            {
                vScroll.Visible = false;
            }

            vScroll.Maximum = yTotal - layersPanel.Bounds.Height + format.ItemHeight;
            vScroll.Minimum = 0;
        }

        private void ResizeItems()
        {
            this.SuspendLayout();
            int prevY = yOffset;

            for (int i = 0; i < items.Count; i++)
            {
                items[i].Layer.Width = layersPanel.ClientRectangle.Width - GetScrollWidth();
                items[i].Layer.Location = new Point(0, prevY);

                items[i].Frame.Width = framesPanel.ClientRectangle.Width;
                items[i].Frame.Location = new Point(0, prevY);

                prevY += format.ItemHeight;
            }

            this.ResumeLayout();
        }

        private int GetScrollWidth()
        {
            if (vScroll.Visible) { return vScroll.Bounds.Width; }
            else { return 0; }
        }

        private int GetNextY(int index)
        {
            if (items.Count > index && index > 0)
            {
                return items[index - 1].Layer.Location.Y + format.ItemHeight;
            }
            else { return 0; }
        }

        private void RepositionItems(int startIndex)
        {
            int start = startIndex >= 0 ? startIndex : 0;
            for (int i = start; i < items.Count; i++)
            {
                items[i].Layer.Location = new Point(0, GetNextY(i));
                items[i].Frame.Location = new Point(0, GetNextY(i));
            }
        }

        private void btnAddLayer_Click(object sender, EventArgs e)
        {
            LayerItem li = new LayerItem("test", false, false);
            FrameItem fi = new FrameItem();
            TimelineItem tli = new TimelineItem(li, fi, format);
            items.Add(tli);
			OnLayerAdded();
        }

        private void btnDeleteLayer_Click(object sender, EventArgs e)
        {
			if (selectedIndex >= 0 && items.Count > 0)
			{
				items.RemoveAt(selectedIndex);
				int newIndex;
				if (selectedIndex > items.Count - 1)
				{
					newIndex = items.Count > 0 ? 0 : -1;

				}
				else { newIndex = selectedIndex; }
				SetSelected(newIndex); //set regardless, because the item changed, even if index doesn't

				RepositionItems(selectedIndex);
				OnLayerRemoved();
			}
        }

        private void btnMoveLayerUp_Click(object sender, EventArgs e)
        {
            items.MoveUp(selectedIndex);
			OnLayerMoved();
        }

        private void btnMoveLayerDown_Click(object sender, EventArgs e)
        {
            items.MoveDown(selectedIndex);
			OnLayerMoved();
        }

        private void btnLayerVisible_Click(object sender, EventArgs e)
        {
            if (selectedIndex >= 0 && selectedIndex >= items.Count - 1)
            {
                items[selectedIndex].Layer.Displayed = !items[selectedIndex].Layer.Displayed;
				OnLayerVisibleChanged();
            }
        }

        private void btnLayerLocked_Click(object sender, EventArgs e)
        {
            if (selectedIndex >= 0 && selectedIndex >= items.Count - 1)
            {
                items[selectedIndex].Layer.Locked = !items[selectedIndex].Layer.Locked;
				OnLayerLockChanged();
            }
        }

		private void btnNone_Click(object sender, EventArgs e)
		{

		}

		private void btnOnionSkinLayer_Click(object sender, EventArgs e)
		{

		}

		private void btnOnionSkinAll_Click(object sender, EventArgs e)
		{

		}

        private void splitMain_SplitterMoved(object sender, SplitterEventArgs e)
        {
            timeRuler.XOffset = splitMain.Panel2.Bounds.X;
            ResizeItems();
        }

        protected override void OnClientSizeChanged(EventArgs e)
        {
            this.SuspendLayout();

            for (int i = 0; i < items.Count; i++)
            {
                items[i].Frame.Width = framesPanel.ClientRectangle.Width;
                items[i].Layer.Width = layersPanel.ClientRectangle.Width;
            }

            this.ResumeLayout();
        }

		//private void mnuLoad_Click(object sender, EventArgs e)
		//{
		//    OnFrameLoaded();
		//}

		private void mnuDelete_Click(object sender, EventArgs e)
		{
			OnFrameRemoved();
		}

		private void mnuInsert_Click(object sender, EventArgs e)
		{
			OnFrameAdded();
		}

		private void mnuDuplicate_Click(object sender, EventArgs e)
		{
            List<TimeLineFrame> frames = items[selectedIndex].Frame.Frames;
            int index = items[selectedIndex].Frame.SelectedFrame;

            TimeLineFrame tlf = new TimeLineFrame();
            tlf.StartCell = frames[index].StartCell + frames[index].Span;
            tlf.Span = frames[index].Span;
            items[selectedIndex].Frame.Frames.Add(tlf);

			OnFrameDuplicated();
		}

		private void contextMenuFrames_Opening(object sender, CancelEventArgs e)
		{
			if (items != null && selectedIndex >= 0)	//there must be a selected item
			{
				if (items[selectedIndex].Frame.SelectedFrame >= 0)
				{
					//mnuLoad.Enabled = true;
					mnuDelete.Enabled = true;
					mnuDuplicate.Enabled = true;
				}
				else
				{
					//mnuLoad.Enabled = false;
					mnuDelete.Enabled = false;
					mnuDuplicate.Enabled = false;
				}

				mnuInsert.Enabled = true;
			}
			else
			{
				//mnuLoad.Enabled = false;
				mnuDelete.Enabled = false;
				mnuInsert.Enabled = false;
				mnuDuplicate.Enabled = false;
			}

		}

		private void timeRuler_SelectionChange(object sender, EventArgs e)
		{
			OnRulerSelectionChanged();
		}
    }

	public class TimelineEventArgs : EventArgs
	{
        public int LayerIndex { get; set; }
        public int OriginalLayerIndex { get; set; }
		public int FrameIndex { get; set; }
		public int OriginalFrameIndex { get; set; }
		public int FrameStart { get; set; }
		public int FrameSpan { get; set; }
		public int SelectedFrame { get; set; }
		public int ShiftAmount { get; set; }
        public int OnionSkinFrame { get; set; }
        public bool OnionSkinAll { get; set; }
        public bool OnionSkinCurrent { get; set; }

        public TimelineEventArgs() { }
	}


}
